﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using WindowsGame.Common.Data;
using WindowsGame.Common.Library;
using WindowsGame.Common.Static;

namespace WindowsGame.Common.Objects
{
	public class Sprite
	{
		private readonly IDictionary<Direction, Vector2> velocityData;
		private SnakeData data;
		private IList<Vector2> positions;
		private Direction direction;
		private Int32 timeAtTile;
		private Int32 tileAtMove;
		private readonly Int32 scale;
		private readonly Int32 block;
		private readonly Int32 tailLength;
		private readonly Int32 timeOnTile;
		private readonly Int32 tileToMove;
		private readonly Int32 noCols;
		private readonly Int32 noRows;

		public Sprite(SnakeData theData, BoardType size, Int32 noCols, Int32 noRows)
		{
			data = theData;
			velocityData = GetVelocity();

			tailLength = data.TailLength;
			Int32 maxLength = 4 / (Int32)size * 10;
			if (tailLength < 0) { tailLength = 0; }
			if (tailLength > maxLength) { tailLength = maxLength; }

			this.noCols = noCols;
			this.noRows = noRows;
			scale = (Int32)size;
			block = scale * 8;

			timeOnTile = data.TimeOnTile;
			tileToMove = data.TileToMove;

			Reset();
		}

		public void Update(GameTime gameTime)
		{
			timeAtTile += gameTime.ElapsedGameTime.Milliseconds;
			if (timeAtTile < timeOnTile)
			{
				return;
			}

			// Reset clock and move.
			timeAtTile -= timeOnTile;

			// Check number times same theDirection.
			if (tileAtMove >= tileToMove)
			{
				tileAtMove = 0;
				direction = ChangeDirection(direction);
			}
			tileAtMove++;

			// Begin head.
			Vector2 velocity = velocityData[direction];
			Vector2 oldHead = positions[0];
			Vector2 tmpHead = oldHead + velocity;

			// Move tail.
			for (int idx = tailLength; idx >= 1; --idx)
			{
				positions[idx] = positions[idx - 1];
			}

			// End head.
			tmpHead = CheckBoundariesWrap(tmpHead);
			positions[0] = tmpHead;
		}

		public void Draw()
		{
			for (int idx = 1; idx <= tailLength; ++idx)
			{
				DrawTail(idx);
			}
			DrawHead();
		}

		public void Reset()
		{
			InitializePositions();
			direction = data.Direction;

			timeAtTile = timeOnTile;
			tileAtMove = 0;
		}

		private void InitializePositions()
		{
			positions = new List<Vector2>();
			for (int idx = 0; idx <= tailLength; ++idx)
			{
				positions.Add(Vector2.Zero);
			}

			Vector2 tmpHead = new Vector2(data.StartPosX, data.StartPosY);
			tmpHead = CheckBoundariesStop(tmpHead);
			positions[0] = tmpHead;

			Vector2 offset = -1 * velocityData[data.Direction];
			for (int idx = 1; idx <= tailLength; ++idx)
			{
				Vector2 tail = offset * idx;
				positions[idx] = tmpHead + tail;
			}
		}

		private void DrawHead()
		{
			Draw(0, 2, Color.White);
		}
		private void DrawTail(Int32 idx)
		{
			Draw(idx, 1, Color.Red);
		}
		private void Draw(Int32 idx, Int32 mul, Color color)
		{
			Engine.SpriteBatch.Draw(Assets.BlobTexture, positions[idx] * block, null, color, 0, Vector2.Zero, scale * mul, SpriteEffects.None, 1);
		}

		private Vector2 CheckBoundariesStop(Vector2 tmpHead)
		{
			if (tmpHead.X < 0) { tmpHead.X = 0; }
			if (tmpHead.X > noCols - 1) { tmpHead.X = noCols - 1; }

			if (tmpHead.Y < 0) { tmpHead.Y = 0; }
			if (tmpHead.Y > noRows - 1) { tmpHead.Y = noRows - 1; }

			return tmpHead;
		}
		private Vector2 CheckBoundariesWrap(Vector2 tmpHead)
		{
			if (tmpHead.X < 0) { tmpHead.X = noCols - 1; }
			if (tmpHead.X > noCols - 1) { tmpHead.X = 0; }

			if (tmpHead.Y < 0) { tmpHead.Y = noRows - 1; }
			if (tmpHead.Y > noRows - 1) { tmpHead.Y = 0; }

			return tmpHead;
		}

		private Direction ChangeDirection(Direction theDirection)
		{
			Int32 currDirection = (Int32)theDirection;
			Int32 nextDirection;
			while (true)
			{
				nextDirection = MyGame.Manager.NumberManager.Generate(4);
				if (currDirection == 0 && nextDirection != 1) { break; }
				if (currDirection == 1 && nextDirection != 0) { break; }
				if (currDirection == 2 && nextDirection != 3) { break; }
				if (currDirection == 3 && nextDirection != 2) { break; }
			}

			return (Direction)nextDirection;
		}

		private static IDictionary<Direction, Vector2> GetVelocity()
		{
			return new Dictionary<Direction, Vector2>
			{
				{Direction.Left, new Vector2(-1, 0)},
				{Direction.Right, new Vector2(1, 0)},
				{Direction.Up, new Vector2(0, -1)},
				{Direction.Down, new Vector2(0, 1)}
			};
		}

	}
}